#include "random_in.h"
#include <string.h>
#include <pthread.h>

const char* optstr = "linx-info.com";
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

int linx_random_init()
{
    unsigned char e[32];
    int ret = get_entropy(e, 32);
    if(ret) return ret;
    return _linx_random_init(e, 32, optstr, strlen(optstr));
}

int linx_random_reseed(void* opt, const int len)
{
    unsigned char e[32];
    int ret = get_entropy(e, 32);
    if(ret) return ret;
    return _linx_random_reseed(e, 32, optstr, strlen(optstr));
}

int linx_random(unsigned char* random, const unsigned int len)
{
    int ret = 0;
    pthread_mutex_lock(&lock);
    ret = _linx_random(NULL, 0, random, len);
    pthread_mutex_unlock(&lock);
    return ret;
}

int linx_random_recount_test()
{
    return _recount_test();
}

int linx_random_adaptive_scale_test()
{
    return _adaptive_scale_test();
}

/**
 * @brief 随机数生成初始化
 * 
 * @param nonce 噪音
 * @param nlen 噪音长度
 * @param str 可选个性化字符串输入
 * @param strlen 字符串长度
 * @return int 成功返回0，或对应错误码。
 */
int _linx_random_init(const void* nonce, const int nlen,
    const char* str, const int strlen)
{
    int ret = 0;

    ret = sm3_rng_instantiate(str, strlen, nonce, nlen);
    if(ret){
        random_state_set(RANDOM_STATE_ERROR);
        return ret;
    }
    random_state_set(RANDOM_STATE_WORK);
    return ret;
}

/**
 * @brief 随机数重播种
 * 
 * @param es 熵输入
 * @param eslen 熵长度
 * @param ex 可选额外输入
 * @param ex_len 长度
 * @return int 成功返回0，或对应错误码。
 */
int _linx_random_reseed(const void* es, const int eslen,
    const void* ex, const int ex_len)
{
    int ret = 0;
    if(!random_state_is(RANDOM_STATE_WORK))
        return ERANDOM_STATE;
    ret = sm3_rng_reseed(es, eslen, ex, ex_len);
    if(ret) random_state_set(RANDOM_STATE_ERROR);
    return ret;
}

/**
 * @brief 随机数生成
 * 
 * @param ex_in 可选额外输入
 * @param len 输入长度
 * @param random 随机数输出buf
 * @param rlen 输出随机数长度
 * @return int 成功返回0，或对应错误码。
 */
int _linx_random(const void* ex_in, const int len,
    unsigned char* random, const unsigned int rlen)
{
    int ret = 0;
    int loop = rlen / 32;
    int left = rlen - loop*32;
    unsigned char temp[32];

    if(!random_state_is(RANDOM_STATE_WORK))
        return ERANDOM_STATE;
    
    for (int i = 0; i < loop; i++) {
        ret = sm3_rng_generate(ex_in, len, random+i*32);
        if(ret) goto out;
    }
    if(left > 0) {
        ret = sm3_rng_generate(ex_in, len, temp);
        if(ret) goto out;
        memcpy(random+loop*32, temp, left);
    }
    return ret;
out:
    memset(random, 0, rlen);
    return ret;
}